package org.wikipedia.miner.util;

import java.io.File;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

import com.mysql.jdbc.PreparedStatement;

/**
 * compare two category and calculate the relatedness measures between the two
 * categories
 * 
 * @author nick
 * 
 */
public class MASComparer1 {
	public static WikiDB wikiDB = WikiDB.getDB(WikiDB.DBType.OLDWIKIMINERDB);
	public static int level = 3;
	public static String outputDir = "d:/data/mas1";

	String categoryA, categoryB;

	static public String[] categoryStrings = { "Philosophy", "Logic",
			"Mathematics", "Physics", "Chemistry", "Biology",
			"Computer science", "Psychology", "Linguistics", "Sociology",
			"History", "Economics", "Political science", "Literature" };

	// static public String[] categoryStrings = { "Philosophy", "Logic", };

	/**
	 * given two categories that to be compared and measured
	 * 
	 * @param catA
	 * @param catB
	 */
	public MASComparer1(String catA, String catB) {

		this.categoryA = catA;
		this.categoryB = catB;
	}

	public String getCategoryA() {
		return categoryA;
	}

	public String getCategoryB() {
		return categoryB;
	}

	/**
	 * use inlink to calculate the google distance function
	 * 
	 * @param level
	 * @return
	 */
	public Double getInLinkGoogleMeasure(int level) {
		ArrayList<Integer> l1 = wikiDB.getCategoryLinksIn(categoryA, level);
		ArrayList<Integer> l2 = wikiDB.getCategoryLinksIn(categoryB, level);
		ArrayList<Integer> ilist = getIntersection(l1, l2);
		// XXX may need to getTotalCategoryCount
		double total = wikiDB.getTotalArticleCount(); // wikipedia.getEnvironment().retrieveStatistic(StatisticName.articleCount);

		return getGoogleMeasure(l1.size(), l2.size(), ilist.size(), total);
	}

	ArrayList<Integer> getIntersection(ArrayList<Integer> l1,
			ArrayList<Integer> l2) {
		ArrayList<Integer> ret = new ArrayList<Integer>();
		for (int i = 0; i < l1.size(); i++) {
			int c1 = l1.get(i);
			if (l2.contains(c1)) {
				ret.add(c1);
			}
		}
		return ret;
	}

	/**
	 * public Double getRelatedness(int level) { return
	 * getRelatedness(getInLinkGoogleMeasure(level)); }
	 */

	/**
	 * given a distance measure to calculate the relatedness
	 * 
	 * @param m
	 * @return
	 */
	public static Double getRelatedness(Double distanceMeasure) {
		Double m = distanceMeasure;

		if (m == null)
			return 0.0;
		if (m >= 1.0) {
			return (0.0);
		}
		return (1.0 - m);
	}

	/**
	 * size measure is to calculate the intersection
	 * 
	 * @param level
	 * @return
	 */
	public Double getSizeGoogleMeasure(int level) {
		ArrayList<Integer> l1 = wikiDB.getDistinctDescendentArticles(categoryA,
				level);
		ArrayList<Integer> l2 = wikiDB.getDistinctDescendentArticles(categoryB,
				level);
		ArrayList<Integer> ilist = getIntersection(l1, l2);

		long total = wikiDB.getTotalArticleCount();

		return getGoogleMeasure(l1.size(), l2.size(), ilist.size(), total);
	}

	/**
	 * give the 4 input, calculate the google distance function
	 * 
	 * @param count1
	 * @param count2
	 * @param intersection
	 * @param total
	 * @return
	 */
	public static double getGoogleMeasure(double count1, double count2,
			double intersection, double total) {
		double maxab = Math.max(count1, count2);
		double minab = Math.min(count1, count2);
		double gd = (Math.log(maxab) - Math.log(intersection))
				/ (Math.log(total) - Math.log(minab));
		return gd;
	}

	public static void main(String args[]) throws Exception {

		System.out.println("starting wiki CategoryComparison...");

		System.out.println("retrieving article ...");

		getMeasureMatrix();
	}

	static void getMeasureMatrix() throws Exception {
		int len = categoryStrings.length;

		double[][] articleRelatedness = new double[len][len];

		for (int i = 0; i < len; i++) {
			String cat1 = categoryStrings[i];
			String art1 = categoryStrings[i];
			ArticleCmp7 artComparer = new ArticleCmp7();
			for (int j = (i + 1); j < len; j++) {

				String art2 = wikiDB.getArticleTitleByTitle(categoryStrings[j]);
				String cat2 = (categoryStrings[j]);

				DateFormat dateFormat = new SimpleDateFormat(
						"yyyy/MM/dd HH:mm:ss");
				System.out.println(dateFormat.format(new Date())
						+ " comparing Category " + cat1 + " and Category "
						+ cat2);

				articleRelatedness[i][j] = artComparer.getRelatedness(art1,
						art2);

			}
		}

		File f = new File(outputDir);
		if (!f.exists())
			f.mkdir();
		printCSV(outputDir + File.separator + "ArticleRelatedness.csv",
				articleRelatedness);

	}

	static void printCSV(String fileName, double[][] tbp) throws IOException {
		// String[] categoryStrings = categoryStrings;
		File f = new File(fileName);
		// if file doesnt exists, then create it
		if (!f.exists()) {
			f.createNewFile();
		}

		FileWriter fw = new FileWriter(f);
		// print the first row
		String headline = "Categories,";
		for (int i = 0; i < categoryStrings.length; i++) {
			headline += "\"" + categoryStrings[i] + "\"";
			if (i < (categoryStrings.length - 1))
				headline += ",";
		}
		headline += "\r\n";
		fw.write(headline);

		for (int i = 0; i < categoryStrings.length; i++) {
			String strline = categoryStrings[i] + ",";
			for (int j = 0; j < categoryStrings.length; j++) {
				strline += tbp[i][j];
				if (j < (categoryStrings.length - 1))
					strline += ",";
			}
			strline += "\r\n";
			fw.write(strline);
		}

		fw.close();
	}

	public static void commandLine() throws Exception {
		LineNumberReader inreader = new LineNumberReader(new InputStreamReader(
				System.in));
		while (true) {
			String str1 = null;
			String art1 = null;
			String cat1 = null;
			while (str1 == null || art1 == null || cat1 == null) {
				System.out.print("input Category A: ");
				str1 = inreader.readLine();
				if (str1 == null || str1.length() == 0)
					continue;
				art1 = wikiDB.getArticleTitleByTitle(str1);
				cat1 = str1;
			}
			String str2 = null;
			String art2 = null;
			String cat2 = null;
			while (str2 == null || art2 == null || cat2 == null) {
				System.out.print("input Category B: ");
				str2 = inreader.readLine();
				if (str2 == null || str2.length() == 0)
					continue;
				art2 = wikiDB.getArticleTitleByTitle(str2);
				cat2 = str2; // wikipedia.getCategoryByTitle(str2);
			}
			ArticleCmp7 artComparer = new ArticleCmp7();

			Double relatedness = artComparer.getRelatedness(art1, art2);
			System.out.println("Article comparison: " + art1 + " is "
					+ relatedness + " related to " + art2);

			MASComparer1 cmp = new MASComparer1(cat1, cat2);
			Double crel = MASComparer1.getRelatedness(cmp
					.getInLinkGoogleMeasure(1));
			System.out.println("Category comparison level1: " + cat1 + " is "
					+ crel + " related to " + cat2);
			crel = MASComparer1.getRelatedness(cmp.getInLinkGoogleMeasure(2));
			System.out.println("Category comparison level2: " + cat1 + " is "
					+ crel + " related to " + cat2);

			crel = getRelatedness(cmp.getSizeGoogleMeasure(2));
			System.out
					.println("Category size and intersection comparison level2: "
							+ cat1 + " is " + crel + " related to " + cat2);
			crel = getRelatedness(cmp.getSizeGoogleMeasure(3));
			System.out
					.println("Category size and intersection comparison level3: "
							+ cat1 + " is " + crel + " related to " + cat2);

		}
	}

}

/**
 * mainly to compare the output with getRelatedness at Wikiminer
 * 
 * @author nick
 * 
 */
class ArticleCmp7 {

	// String articleA, articleB;

	public ArticleCmp7() {// String artA, String artB) {
		// this.articleA = artA;
		// this.articleB = artB;
	}

	public Double getInLinkGoogleMeasure(String articleA, String articleB) {
		ArrayList<Integer> l1 = getArr(articleA);
		ArrayList<Integer> l2 = getArr(articleB);
		ArrayList<Integer> ilist = getIntersection(l1, l2);
		double total = MASComparer1.wikiDB.getTotalArticleCount(); // wikipedia.getEnvironment().retrieveStatistic(StatisticName.articleCount);
		System.out.println(articleA + ":" + l1.size() + "_" + articleB + ":"
				+ l2.size() + "_" + ilist.size());
		return getGoogleMeasure(l1.size(), l2.size(), ilist.size(), total);
	}

	public ArrayList<Integer> getArr(String str) {
		ArrayList<Integer> arr = new ArrayList<Integer>();
		String driver = "com.mysql.jdbc.Driver";
		String url = "jdbc:mysql://192.168.0.210/microsoftacademicgraph";
		String user = "root";
		String password = "root";

		try {
			Class.forName(driver);
			Connection conn = DriverManager.getConnection(url, user, password);
			conn.setAutoCommit(false);
			// String sql = "select a.`paper reference id` from `major_" + str
			// + "` a";
			 String sql = "select a.`paper id` from `major1_" + str + "` a";
			PreparedStatement pst = (PreparedStatement) conn
					.prepareStatement(sql);
			ResultSet rs = pst.executeQuery();
			while (rs.next()) {
				if (!arr.contains(rs.getInt(1))) {
					arr.add(rs.getInt(1));
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return arr;
	}

	ArrayList<Integer> getIntersection(ArrayList<Integer> l1,
			ArrayList<Integer> l2) {
		ArrayList<Integer> ret = new ArrayList<Integer>();
		for (int i = 0; i < l1.size(); i++) {
			int c1 = l1.get(i);
			if (l2.contains(c1)) {
				ret.add(c1);
			}
		}
		return ret;
	}

	/**
	 * calculate the relatedness of two articles
	 */
	public Double getRelatedness(String art1, String art2) {
		return getRelatedness(getInLinkGoogleMeasure(art1, art2));
	}

	public static Double getRelatedness(Double m) {
		if (m == null)
			return 0.0;
		if (m >= 1.0) {
			return (0.0);
		}
		return (1.0 - m);
	}

	public Double getSizeGoogleMeasure(String art1, String art2) {
		ArrayList<String> l1 = MASComparer1.wikiDB.getArticleLinksOut(art1, 1);
		ArrayList<String> l2 = MASComparer1.wikiDB.getArticleLinksOut(art2, 1);

		ArrayList<String> ilist = new ArrayList<String>();
		for (int i = 0; i < l1.size(); i++) {
			String c1 = l1.get(i);
			if (l2.contains(c1)) {
				ilist.add(c1);
			}
		}

		long total = MASComparer1.wikiDB.getTotalArticleCount();
		// double total =
		// wikipedia.getEnvironment().retrieveStatistic(StatisticName.articleCount);
		return getGoogleMeasure(l1.size(), l2.size(), ilist.size(), total);
	}

	public static double getGoogleMeasure(double count1, double count2,
			double intersection, double total) {
		double maxab = Math.max(count1, count2);
		double minab = Math.min(count1, count2);
		double gd = (Math.log(maxab) - Math.log(intersection))
				/ (Math.log(total) - Math.log(minab));
		return gd;
	}
}
